import jsbeautify from "js-beautify";

/**
 * 格式化字符串
 *
 * @param {String} source
 * @return {*}
 */
const Beautify = (source) => {
  var opts = {};
  opts.indent_char = " ";
  opts.preserve_newlines = "-1";
  return jsbeautify.js(source, opts);
};

/**
 * 处理 URL 参数对象
 *
 * @param {*} Obj
 * @param {*} Mapping
 */
const parametersFormat = function (Obj, Mapping) {
  for (let i = 0; i < Obj.length; i++) {
    const element = Obj[i];
    schemaFormat(element, Mapping);
  }
};

var ignoreRequestTypeArray = ["application/json-patch+json", "text/json", "application/*+json", "application/vnd.pgrst.object+json", "text/csv"];

/**
 * 处理Body参数对象
 *
 * @param {*} Obj
 * @param {*} Mapping
 */
const requestBodyFormat = function (Obj, Mapping) {
  const Content = {};
  for (const key in Obj.content) {
    const element = Obj.content[key];
    if (!ignoreRequestTypeArray.includes(key)) {
      if (element.schema.$ref) {
        element.schema = Mapping[element.schema.$ref];
      }
      schemaFormat(element.schema, Mapping);
      Content[key] = element;
    }
  }
  Obj.content = Content;
};

/**
 * 处理返回值对象
 *
 * @param {*} Obj
 * @param {*} Mapping
 */
const responsesFormat = function (Obj, Mapping) {
  for (const key in Obj) {
    const element = Obj[key];
    const Content = {};
    for (const key in element.content) {
      const item = element.content[key];
      if (key != "text/plain" && key != "text/json") {
        if (item.schema.$ref) {
          item.schema = Mapping[item.schema.$ref];
        }
        schemaFormat(item.schema, Mapping);
        Content[key] = item;
      }
    }
    element.content = Content;
  }
};

/**
 * 合并类型
 *
 * @param {*} schema
 * @param {*} Mapping
 */
const schemaFormat = function (schema, Mapping) {
  if (schema.allOf) {
    var _properties = {};
    var _Str = [];
    var type = "";
    var cache = {};
    for (let i = 0; i < schema.allOf.length; i++) {
      const element = schema.allOf[i];
      const Obj = Mapping[element.$ref];
      if (Obj && Obj.type == "object") {
        type = Obj.type;
        for (const key in Obj.properties) {
          const element = Obj.properties[key];
          _properties[key] = element;
        }
      } else {
        for (const key in Obj) {
          const element = Obj[key];
          cache[key] = element;
        }
      }
      _Str.push(` ${Obj.description || "Object"} `);
    }
    for (const key in schema.properties) {
      const element = schema.properties[key];
      _properties[key] = element;
    }
    delete schema.allOf;
    schema.properties = _properties;
    schema.type = type;

    for (const key in cache) {
      const element = cache[key];
      schema[key] = element;
    }

    if (!schema.description) {
      schema.description = "Object";
    }
    schema.description += " =>" + _Str.join("、");
  }
  if (schema.items && schema.items.$ref) {
    schema.items = Mapping[schema.items.$ref];
  }
  if (schema.oneOf) {
    var _oneOf = [];
    for (let i = 0; i < schema.oneOf.length; i++) {
      const element = schema.oneOf[i];
      if (element.$ref) {
        _oneOf.push(Mapping[element.$ref]);
      } else {
        _oneOf.push(element);
      }
    }
    schema.oneOf = _oneOf;
  }
  for (const key in schema.properties) {
    const element = schema.properties[key];
    schemaFormat(element, Mapping);
  }
};

/**
 * 格式化对象
 *
 * @param {JSON} File Swagger 文档
 */
const FormatJSON = function (File) {
  var Mapping = {};
  // 解析 $ref 链接
  for (const key in File.components.schemas) {
    const element = File.components.schemas[key];
    Mapping["#/components/schemas/" + key] = element;
  }

  for (const key in Mapping) {
    const element = Mapping[key];
    schemaFormat(element, Mapping);
  }

  for (const key in File.paths) {
    const element = File.paths[key];
    for (const key in element) {
      const item = element[key];
      item.parameters && parametersFormat(item.parameters, Mapping);
      item.requestBody && requestBodyFormat(item.requestBody, Mapping);
      item.responses && responsesFormat(item.responses, Mapping);
    }
  }

  return File;
};

/**
 * 格式化Url参数
 *
 * @param {*} Obj
 * @return {*}
 */
const parametersToString = (Obj) => {
  var HeaderStr = [];
  var QueryStr = [];
  QueryStr.push("{\n");
  HeaderStr.push("{\n");
  for (let i = 0; i < Obj.length; i++) {
    const element = Obj[i];
    if (element.in == "header") {
      HeaderStr.push(`${element.name}: ` + FormatSchema(element));
    } else {
      QueryStr.push(`${element.name}: ` + FormatSchema(element));
    }
  }
  QueryStr.push("}\n");
  HeaderStr.push("}\n");
  return {
    query: QueryStr.length == 2 ? null :  Beautify(QueryStr.join("")),
    header: HeaderStr.length == 2 ? null : Beautify(HeaderStr.join("")),
  };

  // return Beautify(QueryStr.join(""));
};

/**
 * 格式化请求数据
 *
 * @param {*} Obj
 * @return {*}
 */
const requestBodyToString = (Obj) => {
  var _Obj = {};
  for (const key in Obj.content) {
    const element = Obj.content[key];
    _Obj[key] = Beautify(FormatSchema(element.schema));
  }
  return _Obj;
};

/**
 * 格式化返回值方法
 *
 * @param {*} Obj
 * @return {*}
 */
const responsesToString = (Obj) => {
  if (Obj[200] && Obj[200].content["application/json"] && Obj[200].content["application/json"].schema) {
    return Beautify(FormatSchema(Obj[200].content["application/json"].schema));
  } else {
    return "";
  }
};

/**
 * 旧版格式化名称的方法
 *
 * @param {*} path
 * @param {*} method
 * @param {*} NameCache
 * @return {*}
 */
const FormarJSDocName = (path, method, NameCache) => {
  var EventKey = path.split("/");
  EventKey = EventKey.slice(3, EventKey.length).join("_");
  EventKey = EventKey.length ? EventKey : "root";
  if (NameCache.includes(EventKey)) {
    EventKey += "_" + method;
  }
  NameCache.push(EventKey);
  return EventKey;
};

/**
 * 新版格式化 Event 名称的方法
 *
 * @param {*} path
 * @param {*} method
 * @param {*} tags
 * @param {*} FileName
 * @param {*} ignore
 * @param {*} NameCache
 * @return {*}
 */
const FormarJSDocNameWithNewRulls = (path, method, tags = [], FileName, ignore, NameCache) => {
  var _path = path.split("/");
  var pathArray = _path.filter((x) => {
    return !!x && !tags.includes(x) && !ignore.includes(x) && !!x && x != FileName;
  });
  var EventKey = pathArray.length ? pathArray.join("_") : "root";
  if (NameCache.includes(EventKey)) {
    EventKey += "_" + method;
  }
  NameCache.push(EventKey);
  return EventKey;
};

/**
 * 将格式化好的 Swagger 配置对象格式化为 Event 文档
 *
 * @param {*} path
 * @param {*} item
 * @param {*} methods
 * @param {*} NameCache
 * @param {*} FileName
 * @param {*} config
 * @return {*}
 */
const RenderEventStr = function (path, item, methods, NameCache, FileName, config) {
  if (config.NewNameRule) {
    var EventName = FormarJSDocNameWithNewRulls(path, methods, [], FileName, config.IgnorePath || [], NameCache);
  } else {
    var EventName = FormarJSDocName(path, methods, NameCache);
  }

  var query = "";
  var headers = "";
  if (item.parameters) {
    var parameters = parametersToString(item.parameters);
    if (parameters.query) {
      query = "* // url 参数 \n" + "var query = " + parameters.query;
    }
    if (parameters.header) {
      headers = "\n// header 参数 \n" + "var header = " + parameters.header;
    }
  }

  var body = "";
  var _header = {};
  if (item.requestBody) {
    var requestBody = requestBodyToString(item.requestBody);
    for (const key in requestBody) {
      const element = requestBody[key];
      if (key == "multipart/form-data") {
        _header["Content-Type"] = "multipart/form-data";
      }
      body += "\n// Body参数 -- 类型 : " + key + "\n";
      body += "var bodyData = " + element;
    }
  }

  var responses = "";
  if (item.responses) {
    responses = "\n// 返回值数据模型 \n" + "var responsesData = " + responsesToString(item.responses) + "\n";
  }

  var str = `
    /**
    * ${item.summary || path}
    *
${query ? "    * @param {Object} parmas Url参数\n" : ""}${
    body ? "    * @param {Object} options Body参数\n" : ""
  }    * @param {Blob} loading 是否显示loading
    * @return {Promise} 接口异步传输对象
    * @example 
    ${headers ? headers.replace(/\n/g, "\n    * ") : "*"}
    ${query ? query.replace(/\n/g, "\n    * ") : "*"}
    ${body ? body.replace(/\n/g, "\n    * ") : "*"}
    ${responses ? responses.replace(/\n/g, "\n    * ") : "*"}
    */
    '${EventName}': function (${query ? "parmas" : ""}${body ? (query ? ", options" : "options") : ""}${
    query || body ? ", loading" : "loading"
  }, header = ${JSON.stringify(_header)}) {
        return request.${methods}('${path}', ${query ? "parmas, " : "{}, "}${body ? "options, " : "{}, "}'${config.TypeStr || ""}', loading, header)
    },

`;
  return str;
};

const FormatDoc = (File, FileName, config = {}) => {
  const NameCache = [];
  let Str = "";
  for (const path in File.paths) {
    const methods = File.paths[path];
    for (const method in methods) {
      const Obj = methods[method];
      Str += RenderEventStr(path, Obj, method, NameCache, FileName, config);
    }
  }
  return `${config.formatModuleString || `import request from './request.js'`}

// ${File.info.title}
// ${File.info.description}
// ${File.info.version}

export default {
${Str}
};`;
};

/**
 * 格式化中文备注
 *
 * @param {*} schema // 对象解释结构
 * @param {*} key // 对象类型
 * @return {string}
 */
const FormatDescription = function (schema, key) {
  return ` // ${schema.description || ""} ${key || ""} (${schema.format || schema.type})${schema.nullable ? "" : " 必填"}${
    schema.enum ? " 备选值 ：" + JSON.stringify(schema.enum) : ""
  }`;
};

/**
 * 将对象 schema 格式化为 Json 对象
 *
 * @param {*} schema
 */
const FormatSchema = function (schema, link = []) {
  // 中断死循环
  if (link.includes(schema)) return "";

  // 隔离分支死循环控制
  const _link = link.map((item) => item);

  // 添加分支
  _link.push(schema);

  var Str = [];
  // 合并 oneOf 类型
  if (schema.oneOf) {
    Str.push("{" + FormatDescription(schema) + "\n");
    Str.push("// oneOf 类型 对象\n");
    for (let i = 0; i < schema.oneOf.length; i++) {
      const item = schema.oneOf[i];
      Str.push("// ************************\n");
      Str.push("// OneOf Start    " + FormatDescription(item, item.key) + "\n");
      Str.push("// ************************\n");
      if (item.type == "object") {
        for (const key in item.properties) {
          const element = item.properties[key];
          const _$link = _link.map((item) => item);
          Str.push(`${key}: ` + FormatSchema(element, _$link));
        }
      } else {
        Str.push((item.default || "null") + "," + FormatDescription(item) + "\n");
      }
      Str.push("// ************************\n");
      Str.push("// OneOf End    " + FormatDescription(item, item.key) + "\n");
      Str.push("// ************************\n");
    }
    Str.push("}," + "\n");
  } else if (schema.type == "object") {
    Str.push("{" + FormatDescription(schema) + "\n");
    for (const key in schema.properties) {
      const element = schema.properties[key];
      Str.push(`${key}: ` + FormatSchema(element, _link));
    }
    Str.push("}," + "\n");
  } else if (schema.type == "array") {
    Str.push("[" + FormatDescription(schema) + "\n");
    Str.push(FormatSchema(schema.items, _link));
    Str.push("]," + "\n");
  } else {
    Str.push((schema.default || "null") + "," + FormatDescription(schema) + "\n");
  }
  return Str.join("");
};

const Render = (File, config = {}, FileName) => {
  return FormatDoc(FormatJSON(File), FileName, config);
};

export default Render;
